package com.baeldung.bidirect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;

import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;

/**
 * Created by dzy on 2018/9/1
 * 双向引用对象的JSON 序列化/反序列化操作
 * 参照教程自己写的类
 * ref : https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
 */
public class Test1 {
  public static void givenBidirectionRelation_whenSerializing_thenException()
    throws JsonProcessingException {

    User user = new User(1, "John");
    Item item = new Item(2, "book", user);
    user.addItem(item);

    String sRtn = new ObjectMapper().writeValueAsString(item);
    System.out.println("item.jsonstr = " + sRtn);

    sRtn = new ObjectMapper().writeValueAsString(user);
    System.out.println("user.jsonstr = " + sRtn);
  }

  private static void givenBidirectionRelation_whenUsingJacksonReferenceAnnotation_thenCorrect() throws JsonProcessingException {
    User user = new User(1, "John");
    Item item = new Item(2, "book", user);
    user.addItem(item);
    String result = new ObjectMapper().writeValueAsString(item);
    System.out.println("因为标注 Item为 引用的前向部分-正常序列化，所以Item的序列化内容包括全部");
    System.out.println("serial item , result = " + result);
    assertThat(result, containsString("book"));
    assertThat(result,containsString("John"));
    assertThat(result,not(containsString("userItems")));

    result = new ObjectMapper().writeValueAsString(user);
    System.out.println("因为标注 User为 引用的后向部分-省略序列化，所以User的序列化内容不包括Items");
    System.out.println("serial user , result = " + result);
  }

  public static void givenBidirectionRelation_whenUsingJsonIdentityInfo_thenCorrect()
    throws JsonProcessingException {
    User2 user =new User2(1,"John");
    Item2 item = new Item2(2,"book",user);
    user.addItem(item);

    String result = new ObjectMapper().writeValueAsString(item);
    System.out.println("Serial Item result = "+result);
    assertThat(result, containsString("book"));
    assertThat(result, containsString("John"));
    assertThat(result, containsString("userItems"));

    result = new ObjectMapper().writeValueAsString(user);
    System.out.println("result = " + result);
  }

  public static void givenBidirectionRelation_whenUsingPublicJsonView_thenCorrect()
    throws JsonProcessingException {
    User3 user = new User3(1,"John");
    Item3 item = new Item3(2,"book",user);
    user.addItem(item);

    // 只输出标注了 Views.Public.class 的 Field
    String result = new ObjectMapper().writerWithView(Views.Public.class)
      .writeValueAsString(item);
    System.out.println("result = " + result);
    assertThat(result,containsString("book"));
    assertThat(result,containsString("John"));
    assertThat(result,not(containsString("userItems")));

    //以下代码将引起Exception
    try {
      result = new ObjectMapper().writerWithView(Views.Internal.class)
        .writeValueAsString(item);
      System.out.println("result = " + result);
    } catch (Exception e){
      System.err.println("throw excetion of StackOverflowError ");
    }
  }

  public static void givenBidirectionRelation_whenUsingCustomSerializer_thenCorrect()
    throws JsonProcessingException {
    User4 user = new User4(1, "John");
    Item4 item = new Item4(2, "book", user);
    user.addItem(item);

    String result = new ObjectMapper().writeValueAsString(item);

    System.out.println("item4 result = " + result);
  }

  /**
   * 可以正确地反序列化
   * @throws JsonProcessingException
   * @throws IOException
   */
  public static void givenBidirectionRelation_whenDeserializingWithIdentity_thenCorrect()
    throws JsonProcessingException, IOException {
    String json =
      "{\"id\":2,\"itemName\":\"book\",\"owner\":{\"id\":1,\"name\":\"John\",\"userItems\":[2]}}";
    ItemWithIdentity item = new ObjectMapper().readerFor(ItemWithIdentity.class).readValue(json);
    assertThat(item.id,equalTo(2));
    assertThat(item.itemName,equalTo("book"));
    assertThat(item.owner.name,equalTo("John"));
    System.out.println("itemWithIdentity = " + item);
  }

  /**
   * 不能完全正确地反序列化：userItems：[2]不能反序列化到对象中
   * @throws JsonProcessingException
   * @throws IOException
   */
  public static void givenBidirectionRelation_whenUsingCustomDeserializer_thenCorrect()
    throws JsonProcessingException, IOException {
    String json =
      "{\"id\":2,\"itemName\":\"book\",\"owner\":{\"id\":1,\"name\":\"John\",\"userItems\":[2]}}";

    ItemWithSerializer item = new ObjectMapper().readerFor(ItemWithSerializer.class).readValue(json);

    assertEquals(2, item.id);
    assertEquals("book", item.itemName);
    assertEquals("John", item.owner.name);

    System.out.println("item = " + item);
  }

  public static void testJsonToMap(){
    ObjectMapper om = new ObjectMapper();
    String json =
      "{\"id\":2,\"itemName\":\"book\",\"owner\":{\"id\":1,\"name\":\"John\",\"userItems\":[2]}}";
    try {
      HashMap<String, Object> rDefHashMap = om.readValue(json, new TypeReference<HashMap<String, Object>>() {});
      System.out.println("rDefHashMap = " + rDefHashMap);

      StringWriter sw = new StringWriter();
      om.writeValue(sw,rDefHashMap);
      System.out.println("sw = " + sw);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public static void testXMLToMap(){
    XmlMapper xmlMapper = new XmlMapper();

  }
  public static void main(String[] args) {
    try {
      givenBidirectionRelation_whenSerializing_thenException();
      givenBidirectionRelation_whenUsingJacksonReferenceAnnotation_thenCorrect();
      givenBidirectionRelation_whenUsingJsonIdentityInfo_thenCorrect();
      givenBidirectionRelation_whenUsingPublicJsonView_thenCorrect();
      givenBidirectionRelation_whenUsingCustomSerializer_thenCorrect();
      givenBidirectionRelation_whenDeserializingWithIdentity_thenCorrect();
      givenBidirectionRelation_whenUsingCustomDeserializer_thenCorrect();
      testJsonToMap();
    } catch (JsonProcessingException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
